/**
 *  @file QueueT.h
 */
#ifndef _QUEUET_H_
#define _QUEUET_H_

#include "../common/CppMemPool.h"
#include "ListT.h"
#include "../threading/SyncLock.h"
#include "../threading/SyncWrap.h"

namespace cppflib
{

namespace collections
{

/**
 *  A First-In-First-Out queue for primitive types (int, char, ...)
 */
template <class TYPE>
class QueueT : public BaseObject
{
private:
   ListT<TYPE> * pList;
   threading::SyncLock *pLock;

   QueueT(void)
   {
      pList = NULL;
      pLock = NULL;
   }
public:

   /**
    *  @param [in] isThreadSafe -- true to make this queue thread safe
    */
   explicit QueueT(bool isThreadSafe)
   {
      this->pList = new ListT<TYPE>();
      cppmempool::Retain(this->pList);

      if (isThreadSafe) {
         pLock = new threading::SyncLock();
         cppmempool::Retain(this->pLock);
      }
      else {
         pLock = NULL;
      }
   }

   virtual ~QueueT()
   {
      Clear();
      cppmempool::Release(pList);
      if (pLock)
         cppmempool::Release(pLock);
   }

   /**
    *  Add data to tail of queue
    */
   void Enqueue(TYPE data)
   {
      threading::SyncWrap<threading::SyncLock> wrap(pLock);
      pList->AddTail(data);
   }

   /** 
    *  Remove from head
    */
   TYPE Dequeue(void)
   {
      threading::SyncWrap<threading::SyncLock> wrap(pLock);
      TYPE data = pList->RemoveAt(0);
      return data;
   }

   /** 
    *  Take a look at head without removing
    */
   TYPE Peek(void)
   {
      threading::SyncWrap<threading::SyncLock> wrap(pLock);
      return pList->Get(0);
   }

   /** 
    *  Get number of elements
    */
   u32_t GetCount(void)
   {
      threading::SyncWrap<threading::SyncLock> wrap(pLock);
      return pList->GetCount();
   }

   /** 
    *  Test whether an element exists
    */
   bool Contains(TYPE data)
   {
      threading::SyncWrap<threading::SyncLock> wrap(pLock);
      for (int i = 0 ; i < static_cast<int>(pList->GetCount()); ++i)
         if (data == pList->Get(i))
            return true;

      return false;
   }

   /**
    *  Clear whole queue
    */
   void Clear(void)
   {
      threading::SyncWrap<threading::SyncLock> wrap(pLock);
      pList->Clear();
   }

};


/**
 *  Queue for BaseObject derived classes
 */
template <class TYPE>
class QueueCppObjT : public BaseObject
{
private:
   ListCppObjT<TYPE> * pList;
   threading::SyncLock *pLock;

   QueueCppObjT(void)
   {
      pList = NULL;
      pLock = NULL;
   }

public:

   /**
    *  @param [in] isThreadSafe -- true to make this queue thread safe
    */
   explicit QueueCppObjT(bool isThreadSafe)
   {
      this->pList = new ListCppObjT<TYPE>();
      cppmempool::Retain(this->pList);

      if (isThreadSafe) {
         pLock = new threading::SyncLock();
         cppmempool::Retain(this->pLock);
      }
      else {
         pLock = NULL;
      }
   }

   virtual ~QueueCppObjT()
   {
      Clear();
      cppmempool::Release(pList);
      if (pLock)
         cppmempool::Release(pLock);
   }

   /**
    *  Add data to tail of queue
    */
   void Enqueue(TYPE *data)
   {
      threading::SyncWrap<threading::SyncLock> wrap(pLock);
      if (data)
         pList->AddTail(data);
   }

   /** 
    *  Remove from head (throws exception if queue is empty)
    *
    *  @note caller should release data returned
    */
   TYPE *Dequeue(void)
   {
      threading::SyncWrap<threading::SyncLock> wrap(pLock);
      TYPE* data = pList->Get(0);
      cppmempool::Retain(data); // retain first so that RemoveAt will only delete the slot but not data itself
      pList->RemoveAt(0);
      return data;
   }

   /** 
    *  Take a look at head without removing (throws exception if queue is empty)
    */
   TYPE *Peek(void)
   {
      threading::SyncWrap<threading::SyncLock> wrap(pLock);
      return pList->Get(0);
   }

   /** 
    *  Get number of elements
    */
   u32_t GetCount(void)
   {
      threading::SyncWrap<threading::SyncLock> wrap(pLock);
      return pList->GetCount();
   }

   /** 
    *  Test whether an element exists
    */
   bool Contains(const TYPE &data)
   {
      threading::SyncWrap<threading::SyncLock> wrap(pLock);

      for (int i = 0 ; i < static_cast<int>(pList->GetCount()); ++i)
         if (data.Equals(*(pList->Get(i))))
            return true;
      
      return false;
   }

   /**
    *  Clear whole queue
    */
   void Clear(void)
   {
      threading::SyncWrap<threading::SyncLock> wrap(pLock);
      pList->Clear();
   }


};

/**
 *  Declare some queue factory type
 */
typedef QueueT<char> QueueChar;
typedef QueueT<u8_t> QueueByte;
typedef QueueT<wc_t> QueueWChar;
typedef QueueT<short> QueueShort;
typedef QueueT<u16_t> QueueUShort;
typedef QueueT<int> QueueInt;
typedef QueueT<u32_t> QueueUInt;
typedef QueueT<long> QueueLong;
typedef QueueT<unsigned long> QueueULong;
typedef QueueT<i64_t> QueueInt64;
typedef QueueT<u64_t> QueueUInt64;
typedef QueueT<bool> QueueBool;
typedef QueueT<float> QueueFloat;
typedef QueueT<double> QueueDouble;
typedef QueueT<ptr_t> QueuePtr;

} // end of namespace collections


} // end of namespace cppflib



#endif
